package javagame;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import javax.swing.*;

public class Game implements Runnable {
    // Framerate variables...
    private int speed;
    private long frame_time;
    private long debt;
    private boolean running;
    
    // Drawing surface / window variables...
    private int width, height;
    private Canvas canvas;
    private BufferStrategy strategy;
    private JFrame frame;
    private JPanel panel;
    private Color bgcolor;
    private Picture bg;
    private Input input;
    
    // Resource management variables...
    private Vector<Entity> entities = new Vector<Entity>();
    private HashMap<String, Picture> pictures = new HashMap<String, Picture>();
    
    public Game() {
        setBackgroundColor(Color.black);
        frame = new JFrame("Title");
        panel = (JPanel) frame.getContentPane();
        canvas = new Canvas();
        canvas.setIgnoreRepaint(true);
        input = new Input();
        canvas.addKeyListener(input);
        canvas.addMouseListener(input);
        canvas.addMouseMotionListener(input);
        panel.add(canvas);
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                quit();
            }
        });
        setSpeed(30);
        setSize(640, 480);
        frame.setVisible(true);
        canvas.createBufferStrategy(2);
        strategy = canvas.getBufferStrategy();
        (new Thread(this)).start();
    }
    
    public Input getInput() {
        return input;
    }
    
    public void setBackgroundColor(Color c) {
        bg = null;
        bgcolor = c;
    }
    
    public void setBackgroundPicture(Picture p) {
        bg = p;
    }
    
    public void setCaption(String c) {
        frame.setTitle(c);
    }
    
    public String getCaption() {
        return frame.getTitle();
    }
        
    public void setSpeed(int _speed) {
        speed = _speed;
        frame_time = 1000 / speed;
        debt = 0;
    }
    
    public int getSpeed() {
        return speed;
    }
    
    public void setSize(int w, int h) {
        width = w;
        height = h;
        frame.setResizable(true);
        canvas.setSize(width, height);
        frame.pack();
        frame.setResizable(false);
    }
    
    public int getWidth() {
        return width;
    }
    
    public int getHeight() {
        return height;
    }
    
    public void addEntity(Entity e) {
        if(!entities.contains(e))  {
            entities.add(e);
            e.register(this);
            Collections.sort(entities);
            e.create();
        }
    }
    
    public void resort() {
        Collections.sort(entities);
    }
    
    public void removeEntity(Entity e) {
        if(entities.contains(e)) {
            e.deregister();
            entities.remove(e);
        }
    }
    
    public void addPicture(String ref, Picture s) {
        if(!pictures.containsKey(ref)) {
            pictures.put(ref, s);
        }
    }
    
    public Picture getPicture(String ref) {
        if(pictures.containsKey(ref)) {
            return pictures.get(ref);
        } else {
            return null;
        }
    }
    
    public void removePicture(String ref) {
        if(pictures.containsKey(ref)) {
            pictures.remove(ref);
        }
    }
    
    public void run() {
        running = true;
        while(running) {
            long begin = System.currentTimeMillis();
            update();
            if(debt >= frame_time) {
                // skip a frame because we are behind
                debt -= frame_time;
            } else {
                draw();
            }
            long elapsed = System.currentTimeMillis() - begin;
            if(elapsed <= frame_time) {
                try {
                    Thread.sleep(frame_time - elapsed);
                } catch(InterruptedException e) {
                    //
                }
            } else {
                debt += frame_time - elapsed;
            }
        }
    }
    
    public void quit() {
        running = false;
    }
    
    public void update() {
        Entity e;
        for(int i = 0; i < entities.size(); i++) {
            e = entities.get(i);
            if(e.isSolid()) {
                for(int j = 0; j < entities.size(); j++) {
                    if(entities.get(j).isSolid()) {
                        if(e.collidesWith(entities.get(j))) {
                            e.collide(entities.get(j));
                        }
                    }
                }
            }
            e.tick(frame_time);
        }
    }
    
    public void draw() {
        Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
        
        // Fill background color.
        if(bg == null) {
            g.setColor(bgcolor);
            g.fillRect(0, 0, width, height);
        } else {
            g.drawImage(bg.getFrame(0), 0, 0, bg.getWidth(), bg.getHeight(), null);
        }
        
        // Draw visible entities.
        for(int i = 0; i < entities.size(); i++) {
            if(entities.get(i).isVisible()) {
                entities.get(i).draw(g);
            }
        }
        
        g.dispose();
        strategy.show();
    }
}